using AutoMapper;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using OnlineEducation.Data;
using OnlineEducation.Data.Entities;
using OnlineEducation.Extensions;
using OnlineEducation.ExternalService;
using OnlineEducation.Helpers;
using OnlineEducation.Hubs;
using OnlineEducation.Job;
using OnlineEducation.Quartz;
using System;
using System.Text;

namespace OnlineEducation
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;

            });
            //configure mysql connection.
            var connection = Configuration.GetConnectionString("DefaultConnection");
            services.AddDbContext<ApplicationDbContext>(options => options.UseMySql(connection).EnableSensitiveDataLogging());

            //configure Identity.
            services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
            {
                // Password settings
                options.Password.RequireDigit = false;
                options.Password.RequiredLength = 6;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
                options.Password.RequireLowercase = false;
                options.Password.RequiredUniqueChars = 1;

                // Lockout settings
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
                options.Lockout.MaxFailedAccessAttempts = 10;
                options.Lockout.AllowedForNewUsers = true;

                // User settings
                options.User.RequireUniqueEmail = false;

                // email confirmation require
                options.SignIn.RequireConfirmedEmail = false;
            })
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddErrorDescriber<CustomIdentityErrorDescriber>()
                .AddDefaultTokenProviders();

            //cookie settings
            services.ConfigureApplicationCookie(options =>
            {
                // Cookie settings
                options.Cookie.HttpOnly = true;
                options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
                options.LoginPath = "/Account/Login"; // If the LoginPath is not set here, ASP.NET Core will default to /Account/Login
                options.LogoutPath = "/Account/Logout"; // If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout
                options.AccessDeniedPath = "/Account/AccessDenied"; // If the AccessDeniedPath is not set here, ASP.NET Core will default to /Account/AccessDenied
                options.SlidingExpiration = true;
            });

            //Get sms configuration options
            services.Configure<SMSOptions>(Configuration.GetSection("SMSOptions"));      
            //inject sms sender
            services.AddTransient<ISMSSender, SMSSender>();

            //get email configuration options
            services.Configure<EmailOptions>(Configuration.GetSection("EmailOptions"));
            //inject email sender.
            services.AddTransient<IEmailSender, EmailSender>();

            //add jwt authentication
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                  .AddCookie(cfg => cfg.SlidingExpiration = true)
                  .AddJwtBearer(jwtBearerOptions =>
                  {
                      jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters()
                      {
                          ValidateActor = false,
                          ValidateAudience = false,
                          ValidateLifetime = true,
                          ValidateIssuerSigningKey = true,
                          ValidIssuer = Configuration["Token:Issuer"],
                          ValidAudience = Configuration["Token:Audience"],
                          IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Token:Key"]))
                      };
                  });

            //add cache configuation
            services.AddDistributedMemoryCache();
            services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromDays(7);
                options.Cookie.Name = ".elearningonline.Session";
                options.Cookie.HttpOnly = true;
                options.Cookie.IsEssential = true;
            });

            // add a service that update the teaching plan status.
            services.AddHostedService<UpdateStatusTaskHostedService>();
            
            services.AddControllersWithViews().AddRazorRuntimeCompilation().AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.ContractResolver = new DefaultContractResolver();
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            }); 

            //add auto mapper configuation
            services.AddAutoMapper(typeof(AutoMapperConfig));

            //add SiganlR service
            services.AddSignalR();

            //set up the Quartz job service.
            services.AddSingleton<IJobScheduler, JobScheduler>();

            //add a Job that creating meeting room.
            services.AddTransient<CreateMeetingJob>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //set a HttpConext accessor
            HttpContextHelper.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {

                app.UseExceptionHandler("/Error/AppError");

                app.UseStatusCodePagesWithReExecute("/Error/Index", "?statusCode={0}");
                // The default HSTS value is 30 days.You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }


            app.UseHttpsRedirection();

            app.UseCookiePolicy();

            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();

            app.UseAuthorization();

            app.UseSession();

            app.UseJobScheduler();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapAreaControllerRoute(
                                name: "areas", "areas",
                                pattern: "{area:exists}/{controller}/{action}/{id?}");

         
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");

                endpoints.MapHub<UpdateClassRoomStatus>("/updateClassRoomStatus");

            });


        }
    }
}
